
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "main.h"
#include "uniqstring.h"
#include "uniqnode.h"
#include "nes.h"
#include "color.h"
#include "pos.h"
#include "options.h"
#include "misc.h"

/* counter for uniq names of dummy nodes in selfedge */
int selfedgecount = 0;

/* number of levels in the graph */
int nlevels = 0;

void *
mgr_malloc (size_t n)
{
  void *ptr;
  if (n == 0)
    {
      return (NULL);
    }
  ptr = malloc (n);
  memset (ptr, 0, n);
  return (ptr);
}

void
mgr_free (void *ptr)
{
  if (ptr)
    {
      free (ptr);
    }
  return;
}

/* cleanup before loading new data */
void
prepare4newdata (void)
{
  if (inputfilename)
    {
      mgr_free (inputfilename);
    }
  inputfilename = (char *) 0;
  nodenumber = 1;
  uniqstring_reset ();
  uniqnode_reset ();
  unode_reset ();
  uedge_reset ();
  usubgraph_reset ();
  pos_clear ();
  bgcr = 0xff;
  bgcg = 0xff;
  bgcb = 0xff;
  selfedgecount = 0;
  return;
}

void
once_init (void)
{
  color_init ();
  return;
}


static void
fold_ins_1node (struct unode *un)
{
  struct dln *newdln = NULL;
  if (option_no_singlenodes)
    {
      if (un->bitflags.singlenode)
	{
	  if (option_debug)
	    {
	      printf ("%s(1): skip because singlenode bit %s\n", __FUNCTION__,
		      un->name);
	    }
	  /* skip single node */
	  return;
	}
      /* insert regular node */
      if (option_selfedges == 0)
	{
	  if (un->bitflags.selfedge)
	    {
	      /* skip selfedge */
	      if (option_debug)
		{
		  printf ("%s(2): skip because selfedge bit %s\n",
			  __FUNCTION__, un->name);
		}
	    }
	  else
	    {
	      newdln = dln_new2 (un);
	      append_wnl (newdln);
	    }
	}
      else
	{
	  newdln = dln_new2 (un);
	  append_wnl (newdln);
	  if (option_debug)
	    {
	      printf ("%s(): added %s inwnl=%d\n", __FUNCTION__, un->name,
		      un->bitflags.inwnl);
	    }
	}
    }
  else
    {
      if (option_selfedges == 0)
	{
	  if (un->bitflags.selfedge)
	    {
	      /* skip selfedge */
	      if (option_debug)
		{
		  printf ("%s(3): skip because selfedge bit %s\n",
			  __FUNCTION__, un->name);
		}
	    }
	  else
	    {
	      newdln = dln_new2 (un);
	      append_wnl (newdln);
	      if (option_debug)
		{
		  printf ("%s(): added %s inwnl=%d\n", __FUNCTION__, un->name,
			  un->bitflags.inwnl);
		}
	    }
	}
      else
	{
	  /* insert all nodes */
	  newdln = dln_new2 (un);
	  append_wnl (newdln);
	  if (option_debug)
	    {
	      printf ("%s(): added %s inwnl=%d\n", __FUNCTION__, un->name,
		      un->bitflags.inwnl);
	    }
	}
    }
  return;
}

static void
fold_ins_nodes (struct dln *nllist)
{
  struct dln *nptr = NULL;
  nptr = nllist;
  while (nptr)
    {
      nptr->un->bitflags.done = 1;
      fold_ins_1node (nptr->un);
      nptr = nptr->next;
    }
  return;
}

static void
fold_ins_1edge (struct uedge *ue)
{
  struct dle *newdle = NULL;

  if (ue->fn->bitflags.inwnl && ue->tn->bitflags.inwnl)
    {
      if (option_selfedges == 0)
	{
	  if (ue->bitflags.selfedge)
	    {
	      /* skip selfedge */
	    }
	  else
	    {
	      newdle = dle_new2 (ue);
	      append_wel (newdle);
	    }
	}
      else
	{
	  newdle = dle_new2 (ue);
	  append_wel (newdle);
	}
    }
  else
    {
      if (option_debug)
	{
	  printf
	    ("%s(): failed fn.inwnl=%d tn.inwnl=%d %s->%s\n",
	     __FUNCTION__, ue->fn->bitflags.inwnl, ue->tn->bitflags.inwnl,
	     ue->fn->name, ue->tn->name);
	}
    }

  return;
}

/* do subgraph starting at rootgraph */
static void
fold_do_1sub (struct usubg *subg, int forcefold)
{
  struct dlsg *sgptr = NULL;
  if (forcefold)
    {
      if (option_debug)
	{
	  printf ("%s(): subgraph %s forced folded\n", __FUNCTION__,
		  subg->name);
	}
      subg->bitflags.folded = 1;
      sgptr = subg->sg;
      while (sgptr)
	{
	  if (sgptr->sg->bitflags.done)
	    {
	      sgptr = sgptr->next;
	      continue;
	    }
	  sgptr->sg->bitflags.done = 1;
	  fold_do_1sub (sgptr->sg, 1);
	  sgptr = sgptr->next;
	}
      return;
    }
  if (subg->bitflags.folded)
    {
      if (subg->rootedon == NULL)
	{
	  /* in rootgraph; only summary node visible */
	  subg->bitflags.visible = 0;
	  subg->bitflags.summary = 1;
	  subg->summaryn->bitflags.done = 1;
	  uniqnode_add (subg->summaryn->name, subg->summaryn);
	  fold_ins_1node (subg->summaryn);
	  sgptr = subg->sg;
	  while (sgptr)
	    {
	      if (sgptr->sg->bitflags.done)
		{
		  sgptr = sgptr->next;
		  continue;
		}
	      sgptr->sg->bitflags.done = 1;
	      fold_do_1sub (sgptr->sg, 1);
	      sgptr = sgptr->next;
	    }
	}
      else
	{
	  /* in folded subgraph */
	  if (subg->rootedon->bitflags.folded)
	    {
	      sgptr = subg->sg;
	      while (sgptr)
		{
		  if (sgptr->sg->bitflags.done)
		    {
		      sgptr = sgptr->next;
		      continue;
		    }
		  sgptr->sg->bitflags.done = 1;
		  fold_do_1sub (sgptr->sg, 1);
		  sgptr = sgptr->next;
		}
	    }
	  else
	    {
	      /* only summary node visible */
	      subg->bitflags.visible = 0;
	      subg->bitflags.summary = 1;
	      subg->summaryn->bitflags.done = 1;
	      uniqnode_add (subg->summaryn->name, subg->summaryn);
	      fold_ins_1node (subg->summaryn);
	    }
	}
    }
  else
    {
      /* whole subgraph visible */
      subg->bitflags.visible = 1;
      subg->bitflags.summary = 0;
      fold_ins_nodes (subg->nl);
      sgptr = subg->sg;
      while (sgptr)
	{
	  if (sgptr->sg->bitflags.done)
	    {
	      sgptr = sgptr->next;
	      continue;
	    }
	  sgptr->sg->bitflags.done = 1;
	  fold_do_1sub (sgptr->sg, 0);
	  sgptr = sgptr->next;
	}
    }
  return;
}

static struct usubg *
fold_walkdown (struct usubg *subg)
{
  struct usubg *sgptr;
  sgptr = subg;
  while (sgptr)
    {
      if (sgptr->bitflags.summary || sgptr->bitflags.visible)
	{
	  break;
	}
      sgptr = sgptr->rootedon;
    }
  return (sgptr);
}


static struct usubg *
fold_walkdownsn (struct usubg *subg)
{
  struct usubg *sgptr = NULL;
  struct usubg *sgptr2 = NULL;
  sgptr = subg;
  while (sgptr)
    {
      if (sgptr->bitflags.summary && sgptr->bitflags.visible)
	{
	  sgptr2 = sgptr;
	  break;
	}
      if (!sgptr->bitflags.folded)
	{
	  break;
	}
      sgptr2 = sgptr;
      sgptr = sgptr->rootedon;
    }
  return (sgptr2);
}


static void
fold_do_1subedge (struct uedge *ue)
{
  struct uedge *newue;
  struct usubg *usg;

  /* */
  if (ue->fn->rootedon == NULL || ue->tn->rootedon == NULL)
    {
      return;
    }

  /* */
  if (ue->fn->rootedon->bitflags.visible
      && !ue->tn->rootedon->bitflags.visible
      && !ue->tn->rootedon->bitflags.summary)
    {
      usg = fold_walkdown (ue->tn->rootedon);
      if (usg)
	{
	  if (ue->fn != usg->summaryn)
	    {
	      newue = uedge_new2 (ue->fn, usg->summaryn);
	      newue->rootedon = ue->rootedon;
	      newue->color = ue->color;
	      newue->style = ue->style;
	      newue->bitflags.inwel = ue->bitflags.inwel;
	      newue->bitflags.inedge = ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	      return;
	    }
	}
    }

  /* */
  if (ue->tn->rootedon->bitflags.visible
      && !ue->fn->rootedon->bitflags.visible
      && !ue->fn->rootedon->bitflags.summary)
    {
      usg = fold_walkdown (ue->fn->rootedon);
      if (usg)
	{
	  if (ue->tn != usg->summaryn)
	    {
	      newue = uedge_new2 (usg->summaryn, ue->tn);
	      newue->rootedon = ue->rootedon;
	      newue->color = ue->color;
	      newue->style = ue->style;
	      newue->bitflags.inwel = ue->bitflags.inwel;
	      newue->bitflags.inedge = ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	      return;
	    }
	}
    }

/* */
  if (ue->fn->rootedon->bitflags.summary
      && !ue->tn->rootedon->bitflags.visible
      && !ue->tn->rootedon->bitflags.summary)
    {
      usg = fold_walkdown (ue->tn->rootedon);
      if (usg)
	{
	  if (ue->fn->rootedon->summaryn != usg->summaryn)
	    {
	      newue = uedge_new2 (ue->fn->rootedon->summaryn, usg->summaryn);
	      newue->rootedon = ue->rootedon;
	      newue->color = ue->color;
	      newue->style = ue->style;
	      newue->bitflags.inwel = ue->bitflags.inwel;
	      newue->bitflags.inedge = ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	      return;
	    }
	}
    }

  /* */
  if (ue->tn->rootedon->bitflags.summary
      && !ue->fn->rootedon->bitflags.visible
      && !ue->fn->rootedon->bitflags.summary)
    {
      usg = fold_walkdown (ue->tn->rootedon);
      if (usg)
	{
	  if (ue->tn->rootedon->summaryn != usg->summaryn)
	    {
	      newue = uedge_new2 (usg->summaryn, ue->tn->rootedon->summaryn);
	      newue->rootedon = ue->rootedon;
	      newue->color = ue->color;
	      newue->style = ue->style;
	      newue->bitflags.inwel = ue->bitflags.inwel;
	      newue->bitflags.inedge = ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	      return;
	    }
	}
    }

  /* */
  if (ue->bitflags.inedge)
    {
      if (ue->fn->rootedon->bitflags.folded)
	{
	  /* skip edge */
	  if (option_debug)
	    {
	      printf ("%s(): skipping %s(%s)->%s(%s) ", __FUNCTION__,
		      ue->fn->name, ue->fn->label,
		      ue->tn->name, ue->tn->label);


	      if (ue->fn->rootedon)
		{
		  printf ("fn:%d:%d ", ue->fn->rootedon->bitflags.visible,
			  ue->tn->rootedon->bitflags.summary);
		}
	      if (ue->tn->rootedon)
		{
		  printf ("tn:%d:%d ", ue->tn->rootedon->bitflags.visible,
			  ue->tn->rootedon->bitflags.summary);
		}

	      printf ("\n");
	    }
	}
      else
	{
	  fold_ins_1edge (ue);
	}
      return;
    }

  /* */
  if (ue->fn->rootedon->bitflags.visible
      && ue->tn->rootedon->bitflags.visible)
    {
      fold_ins_1edge (ue);
      return;
    }

  /* */
  if (ue->fn->rootedon->bitflags.visible &&
      ue->tn->rootedon->bitflags.summary)
    {
      newue = uedge_new2 (ue->fn, ue->tn->rootedon->summaryn);
      newue->rootedon = ue->rootedon;
      newue->color = ue->color;
      newue->style = ue->style;
      newue->bitflags.inwel = ue->bitflags.inwel;
      newue->bitflags.inedge = ue->bitflags.inedge;
      fold_ins_1edge (newue);
      return;
    }

  /* */
  if (ue->fn->rootedon->bitflags.summary
      && ue->tn->rootedon->bitflags.visible)
    {
      newue = uedge_new2 (ue->fn->rootedon->summaryn, ue->tn);
      newue->rootedon = ue->rootedon;
      newue->color = ue->color;
      newue->style = ue->style;
      newue->bitflags.inwel = ue->bitflags.inwel;
      newue->bitflags.inedge = ue->bitflags.inedge;
      fold_ins_1edge (newue);
      return;
    }

  /* */
  if (ue->fn->rootedon->bitflags.summary
      && ue->tn->rootedon->bitflags.summary)
    {
      newue =
	uedge_new2 (ue->fn->rootedon->summaryn, ue->tn->rootedon->summaryn);
      newue->rootedon = ue->rootedon;
      newue->color = ue->color;
      newue->style = ue->style;
      newue->bitflags.inwel = ue->bitflags.inwel;
      newue->bitflags.inedge = ue->bitflags.inedge;
      fold_ins_1edge (newue);
      return;
    }


  if (option_debug)
    {

      printf ("%s(2): skipping %s(%s)->%s(%s) ", __FUNCTION__,
	      ue->fn->name, ue->fn->label, ue->tn->name, ue->tn->label);
      if (ue->fn->rootedon)
	{
	  printf ("fn:%d:%d ", ue->fn->rootedon->bitflags.visible,
		  ue->tn->rootedon->bitflags.summary);
	}
      if (ue->tn->rootedon)
	{
	  printf ("tn:%d:%d ", ue->tn->rootedon->bitflags.visible,
		  ue->tn->rootedon->bitflags.summary);
	}
      printf ("\n");

    }

  return;
}

static void
fold_prsub (void)
{
  struct usubg *sgp;
  struct dln *nptr;
  struct dle *eptr;
  struct dlsg *sgptr;
  const char *fstr;
  printf ("%s(): \n", __FUNCTION__);
  sgp = sgl;
  while (sgp)
    {
      if (sgp->bitflags.folded)
	{
	  fstr = "folded";
	}
      else
	{
	  fstr = "unfolded";
	}
      printf ("subgraph %s is %s\n", sgp->name, fstr);
      if (sgp->summaryn)
	{
	  printf (" subgraph %s has summary node %s label %s\n", sgp->name,
		  sgp->summaryn->name, sgp->summaryn->label);
	}
      else
	{
	  printf (" subgraph %s has no summary node\n", sgp->name);
	}
      nptr = sgp->nl;
      if (nptr)
	{
	  while (nptr)
	    {
	      printf ("  node %s is rooted in subgraph %s\n", nptr->un->name,
		      nptr->un->rootedon->name);
	      nptr = nptr->next;
	    }
	}
      else
	{
	  printf (" subgraph %s has no nodes\n", sgp->name);
	}
      eptr = sgp->el;
      if (eptr)
	{
	  while (eptr)
	    {
	      if (eptr->ue->bitflags.inedge)
		{
		  fstr = "set";
		}
	      else
		{
		  fstr = "cleared";
		}
	      printf
		("  edge %s->%s is defined in subgraph %s inedge is %s\n",
		 eptr->ue->fn->name, eptr->ue->tn->name,
		 eptr->ue->rootedon->name, fstr);
	      eptr = eptr->next;
	    }
	}
      else
	{
	  printf (" subgraph %s has no edges\n", sgp->name);
	}
      sgptr = sgp->sg;
      if (sgptr)
	{
	  while (sgptr)
	    {
	      printf ("  subgraph %s is rooted on subgraph %s\n",
		      sgptr->sg->name, sgp->name);
	      sgptr = sgptr->next;
	    }
	}
      else
	{
	  printf (" subgraph %s has no subgraphs\n", sgp->name);
	}
      sgp = sgp->next;
    }

  return;
}

void
fold (void)
{
  struct dln *nptr = NULL;
  struct uedge *newue = NULL;
  struct usubg *usg = NULL;
  struct dle *eptr = NULL;
  struct usubg *sgp = NULL;

  /* unmark done in all subgraphs */
  sgp = sgl;
  while (sgp)
    {
      if (sgp->label)
	{
	  if (option_nodenames)
	    {
	      sgp->summaryn->label = sgp->summaryn->name;
	    }
	  else
	    {
	      sgp->summaryn->label = sgp->label;
	    }
	}
      sgp->bitflags.done = 0;
      sgp->bitflags.visible = 0;
      sgp->bitflags.summary = 0;
      sgp = sgp->next;
    }
  /* unmark node is in working node list */
  nptr = nl;
  while (nptr)
    {
      nptr->un->bitflags.done = 0;
      nptr->un->bitflags.inwnl = 0;
      nptr = nptr->next;
    }
  eptr = el;
  while (eptr)
    {
      eptr->ue->bitflags.done = 0;
      eptr->ue->bitflags.inwel = 0;
      eptr = eptr->next;
    }
  if (option_debug)
    {
      fold_prsub ();
    }
  /* nodes in rootgraph are always there */
  nptr = nl;
  while (nptr)
    {
      if (nptr->un->rootedon == NULL)
	{
	  nptr->un->bitflags.done = 1;
	  fold_ins_1node (nptr->un);
	}
      nptr = nptr->next;
    }

  /* add regular nodes and summary nodes */
  sgp = sgl;
  while (sgp)
    {
      if (sgp->bitflags.done)
	{
	  sgp = sgp->next;
	  continue;
	}
      if (sgp->rootedon == NULL)
	{
	  sgp->bitflags.done = 1;
	  fold_do_1sub (sgp, 0);
	}
      sgp = sgp->next;
    }

  /* edges in rootgraph are always there */
  eptr = el;
  while (eptr)
    {
      if (eptr->ue->fn->rootedon == NULL && eptr->ue->tn->rootedon == NULL)
	{
	  eptr->ue->bitflags.done = 1;
	  fold_ins_1edge (eptr->ue);
	}
      else if (eptr->ue->fn->rootedon == NULL && eptr->ue->tn->rootedon)
	{
	  /* */
	  if (eptr->ue->tn->rootedon->bitflags.visible)
	    {
	      newue = uedge_new2 (eptr->ue->fn, eptr->ue->tn);
	      newue->rootedon = eptr->ue->rootedon;
	      newue->color = eptr->ue->color;
	      newue->style = eptr->ue->style;
	      newue->bitflags.inwel = eptr->ue->bitflags.inwel;
	      newue->bitflags.inedge = eptr->ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	    }
	  else if (eptr->ue->tn->rootedon->bitflags.summary)
	    {
	      newue =
		uedge_new2 (eptr->ue->fn, eptr->ue->tn->rootedon->summaryn);
	      newue->rootedon = eptr->ue->rootedon;
	      newue->color = eptr->ue->color;
	      newue->style = eptr->ue->style;
	      newue->bitflags.inwel = eptr->ue->bitflags.inwel;
	      newue->bitflags.inedge = eptr->ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	    }
	  else
	    {
	      usg = fold_walkdownsn (eptr->ue->tn->rootedon);
	      if (usg)
		{
		  newue = uedge_new2 (eptr->ue->fn, usg->summaryn);
		  newue->rootedon = eptr->ue->rootedon;
		  newue->color = eptr->ue->color;
		  newue->style = eptr->ue->style;
		  newue->bitflags.inwel = eptr->ue->bitflags.inwel;
		  newue->bitflags.inedge = eptr->ue->bitflags.inedge;
		  fold_ins_1edge (newue);
		}
	      else
		{
		  newue = uedge_new2 (eptr->ue->fn, eptr->ue->tn);
		  newue->rootedon = eptr->ue->rootedon;
		  newue->color = eptr->ue->color;
		  newue->style = eptr->ue->style;
		  newue->bitflags.inwel = eptr->ue->bitflags.inwel;
		  newue->bitflags.inedge = eptr->ue->bitflags.inedge;
		  fold_ins_1edge (newue);
		}
	    }
	  /* ready with this edge */
	  eptr->ue->bitflags.done = 1;
	}
      else if (eptr->ue->fn->rootedon && eptr->ue->tn->rootedon == NULL)
	{
	  /* */
	  if (eptr->ue->fn->rootedon->bitflags.visible)
	    {
	      newue = uedge_new2 (eptr->ue->fn, eptr->ue->tn);
	      newue->rootedon = eptr->ue->rootedon;
	      newue->color = eptr->ue->color;
	      newue->style = eptr->ue->style;
	      newue->bitflags.inwel = eptr->ue->bitflags.inwel;
	      newue->bitflags.inedge = eptr->ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	    }
	  else if (eptr->ue->fn->rootedon->bitflags.summary)
	    {
	      newue =
		uedge_new2 (eptr->ue->fn->rootedon->summaryn, eptr->ue->tn);
	      newue->rootedon = eptr->ue->rootedon;
	      newue->color = eptr->ue->color;
	      newue->style = eptr->ue->style;
	      newue->bitflags.inwel = eptr->ue->bitflags.inwel;
	      newue->bitflags.inedge = eptr->ue->bitflags.inedge;
	      fold_ins_1edge (newue);
	    }
	  else
	    {

	      /* */
	      usg = fold_walkdownsn (eptr->ue->fn->rootedon);
	      if (usg)
		{
		  newue = uedge_new2 (usg->summaryn, eptr->ue->tn);
		  newue->rootedon = eptr->ue->rootedon;
		  newue->color = eptr->ue->color;
		  newue->style = eptr->ue->style;
		  newue->bitflags.inwel = eptr->ue->bitflags.inwel;
		  newue->bitflags.inedge = eptr->ue->bitflags.inedge;
		  fold_ins_1edge (newue);
		}
	      else
		{
		  newue = uedge_new2 (eptr->ue->fn, eptr->ue->tn);
		  newue->rootedon = eptr->ue->rootedon;
		  newue->color = eptr->ue->color;
		  newue->style = eptr->ue->style;
		  newue->bitflags.inwel = eptr->ue->bitflags.inwel;
		  newue->bitflags.inedge = eptr->ue->bitflags.inedge;
		  fold_ins_1edge (newue);
		}
	    }
	  /* ready with this edge */
	  eptr->ue->bitflags.done = 1;
	}
      else
	{
	  /* both is a subgraph */
	}
      eptr = eptr->next;
    }

  /* */
  eptr = el;
  while (eptr)
    {
      if (eptr->ue->bitflags.done)
	{
	  eptr = eptr->next;
	  continue;
	}
      /* edge with connection in subgraph */
      eptr->ue->bitflags.done = 1;
      fold_do_1subedge (eptr->ue);
      eptr = eptr->next;
    }

  return;
}


struct usubg *
find_subgraph_summaryname (char *name)
{
  struct usubg *sgp;
  /* search in all subgraphs */
  sgp = sgl;
  while (sgp)
    {
      if (strcmp (name, sgp->summaryn->name) == 0)
	{
	  break;
	}
      sgp = sgp->next;
    }
  return (sgp);
}


/* End. */
